using System;
using System.Collections.Generic;
using System.Linq;
using Pay.Core;
using Pay.Data;
using Pay.DTO;
using Pay.IService;
using Pay.Service.Cache;
using Pay.TO;

namespace Pay.Service {
    public class TransferService : MerchantBillService, ITransferService, IDisposable {
        private IUnitOfWork _iUnitOfWork;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="iUnitOfWork"></param>
        /// <param name="iSettingService"></param>
        /// <param name="iBillService"></param>
        /// <param name="iMerchantService"></param>
        /// <param name="iPaymentService"></param>
        /// <param name="iPayHtmlService"></param>
        /// <param name="iPayNotifyService"></param>
		public TransferService(IUnitOfWork iUnitOfWork, ISettingService iSettingService, IBillService iBillService,
                                   IMerchantService iMerchantService, IPaymentService iPaymentService,
                                   IPayHtmlService iPayHtmlService, IPayNotifyService iPayNotifyService)
                            : base(iUnitOfWork, iSettingService, iBillService, iMerchantService, iPaymentService, iPayHtmlService, iPayNotifyService) {
            _iUnitOfWork = iUnitOfWork;
        }

        /// <summary>
        /// 查询提现
        /// </summary>
        /// <param name="transferQuery"></param>
        /// <returns></returns>
        public PagedResult<transfer_dto> Get(TransferQuery transferQuery) {
            var filter = PredicateBuilder.True<Transfer>();
            if (!string.IsNullOrEmpty(transferQuery.order_no))
                filter = filter.And(c => c.OrderNo == transferQuery.order_no || c.PayInfo.PayNo == transferQuery.order_no || c.PayInfo.TransactionNo == transferQuery.order_no);
            if (transferQuery.status != TransferStatus.None)
                filter = filter.And(c => c.Status == transferQuery.status);
            if (transferQuery.pay_channel != PayChannel.None)
                filter = filter.And(c => c.PayInfo.PayChannel == transferQuery.pay_channel);
            if (transferQuery.merchant_id > 0)
                filter = filter.And(c => c.MerchantId == transferQuery.merchant_id);
            if (!string.IsNullOrEmpty(transferQuery.merchant_no))
                filter = filter.And(c => c.Binder.MerchantNo == transferQuery.merchant_no);
            if (!string.IsNullOrEmpty(transferQuery.name))
                filter = filter.And(c => c.Binder.Name.Contains(transferQuery.name));
            if (!string.IsNullOrEmpty(transferQuery.mobile))
                filter = filter.And(c => c.Binder.Mobile.Contains(transferQuery.mobile));
            var start_time = transferQuery.start_time.ToUnix();
            if (transferQuery.start_time.HasValue && transferQuery.end_time.HasValue) {
                var end_time = transferQuery.end_time.Value.AddDays(1).ToUnix();
                filter = filter.And(c => c.ApplyTime >= start_time && c.ApplyTime <= end_time);
            } else if (transferQuery.start_time.HasValue && !transferQuery.end_time.HasValue)
                filter = filter.And(c => c.ApplyTime >= start_time);
            else if (!transferQuery.start_time.HasValue && transferQuery.end_time.HasValue) {
                var end_time = transferQuery.end_time.Value.AddDays(1).ToUnix();
                filter = filter.And(c => c.ApplyTime <= end_time);
            }

            return context.Transfers.FromCachePaged<Transfer, transfer_dto>(transferQuery.page, transferQuery.size,
                filter, orderBy => orderBy.OrderByDescending(c => c.ApplyTime));
        }

        /// <summary>
        /// 提现金额，截止昨天
        /// </summary>
        public decimal TransferAmount(PayChannel channel) {
            var dt = DateTime.Parse(DateTime.Now.AddDays(-1).ToShortDateString()).ToUnix();
            var result = context.Transfers.Where(c => c.PayInfo.PayChannel == channel && c.ApplyTime <= dt && c.Status == TransferStatus.Apply).Sum(c => (decimal?)c.Amount);
            return result ?? 0;
        }

        /// <summary>
        /// 通过id查询提现
        /// </summary>
        /// <param name="transfer_id"></param>
        /// <returns></returns>
        public transfer_dto Get(long transfer_id) {
            return context.Transfers.Find(transfer_id).ToDtoObject<transfer_dto>();
        }

        /// <summary>
        /// 通过单号查询提现
        /// </summary>
        /// <param name="order_no"></param>
        /// <returns></returns>
        public transfer_dto Get(string merchant_no, string order_no) {
            return context.Transfers.Where(c => c.Merchant.MerchantNo == merchant_no && c.OrderNo == order_no).FirstOrDefault().ToDtoObject<transfer_dto>();
        }

        /// <summary>
        /// 通过单号查询提现
        /// </summary>
        /// <param name="pay_no"></param>
        /// <returns></returns>
        public transfer_dto GetByPayNo(string merchant_no, string pay_no) {
            return context.Transfers.Where(c => c.Merchant.MerchantNo == merchant_no && c.PayInfo.PayNo == pay_no).FirstOrDefault().ToDtoObject<transfer_dto>();
        }

        /// <summary>
        /// 通过流水号查询提现
        /// </summary>
        /// <param name="transactionNo"></param>
        /// <returns></returns>
        public transfer_dto GetByTran(string transactionNo) {
            return context.Transfers.FirstOrDefault(c => c.PayInfo.TransactionNo == transactionNo).ToDtoObject<transfer_dto>();
        }

        public PagedResult<transfer_batch_dto> Get(TransferBatchQuery transferQuery) {
            var filter = PredicateBuilder.True<TransferBatch>();
            if (!string.IsNullOrEmpty(transferQuery.batch_no))
                filter = filter.And(c => c.BatchNo == transferQuery.batch_no);
            if (transferQuery.status != TransferBatchStatus.None)
                filter = filter.And(c => c.Status == transferQuery.status);

            return context.TransferBatchs.FromCachePaged<TransferBatch, transfer_batch_dto>(transferQuery.page, transferQuery.size,
                                                      filter, orderBy => orderBy.OrderByDescending(c => c.Id));
        }

        /// <summary>
        /// 申请提现
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public int ApplyTransfer(apply_transfer dto) {
            if (context.Transfers.Exist(c => c.OrderNo == dto.order_no && c.Merchant.MerchantNo == dto.merchant_no))
                throw new ServerErrorException("提现单" + dto.order_no + "已存在", 1001);
            var oemer = _iMerchantService.Get(dto.merchant_no);
            var oemer_payments = _iMerchantService.GetPayments(oemer.id);
            var payment = oemer_payments.FirstOrDefault(c => c.payment.channel == dto.pay_channel);
            var settle = payment.settles.First(c => c.settle_type == dto.settle_type && c.code == dto.pay_code);
            //o商结算费和ID
            var single_quota = payment.payment.single_quota; var card_quota = payment.payment.card_quota;
            var merchant_id = oemer.id; var o_settle_fee = settle.settle;
            var pay_name = payment.payment.name;
            var fee = dto.settle_fee == 0 ? 0 : dto.settle_fee / 100M;
            //平台结算费
            var plat_settle = payment.payment.settles.First(c => c.settle_type == dto.settle_type && c.code == dto.pay_code).settle;
            if (fee < settle.settle)
                throw new ServerErrorException("结算费不得低商户", 1005);
            if (!string.IsNullOrEmpty(dto.bind_no)) {
                oemer = _iMerchantService.Get(dto.bind_no);
                payment = oemer_payments.FirstOrDefault(c => c.payment.channel == dto.pay_channel);
                //分润货款提现的代付是没有结算方式，只有O商有，如果小商户自己的结算方式
                if (payment != null) {
                    settle = payment.settles.First(c => c.settle_type == dto.settle_type && c.code == dto.pay_code);
                    fee = settle.settle;
                    single_quota = payment.payment.single_quota; card_quota = payment.payment.card_quota;
                }
            }
            decimal amount = decimal.Parse(dto.tran_amount) / 100 + fee;
            var oem_bill = _iBillService.GetByMerchant(merchant_id);
            //判断oem的代付余额是否够
            if (oem_bill.security < amount)
                throw new ServerErrorException("O商代付余额不足", 1002);
            if ((single_quota > 0 && single_quota > amount) || (card_quota > 0 && card_quota < amount))
                throw new ServerErrorException("申请提现金额超出限额", 1002);
            var bill = _iBillService.GetByMerchant(oemer.id);
            if (bill.freeze)
                throw new ServerErrorException("账户冻结", 1003);
            //验证可提现金额够不够
            if (bill.deposit < amount)
                throw new ServerErrorException("余额不足", 1004);

            //增加提现单
            var model = AddTransfer(dto, merchant_id, oemer, amount, fee, o_settle_fee, pay_name, plat_settle);
            //账单
            TransferAccount(model);

            int result = _iUnitOfWork.Commit();
            return result;
        }

        private Transfer AddTransfer(apply_transfer dto, long merchant_id, merchant_dto merchant, decimal amount, decimal fee, decimal o_settle_fee, string pay_name, decimal plat_settle) {
            Transfer model = new Transfer();
            model.OrderNo = dto.order_no;
            model.ApplyTime = DateTime.Now.ToUnix();
            model.MerchantId = merchant_id;
            model.BindId = merchant.id;
            model.TransferAmount = amount - fee;
            model.Status = TransferStatus.Apply;
            model.AccountName = merchant.setting.auth_info.account_name;
            model.BankName = merchant.setting.auth_info.bank_name;
            model.AccountNo = merchant.setting.auth_info.account_no;
            model.IdCard = merchant.setting.auth_info.id_card;
            model.BusiType = merchant.setting.auth_info.busi_type;
            model.SettleAmount = fee;
            model.MerchantSettle = o_settle_fee;
            model.PlatSettle = plat_settle;
            model.Amount = amount;
            model.Notify = dto.notify;
            model.PayInfo = new PayInfo {
                OrderNo = model.OrderNo, PayNo = model.OrderNo, CreateTime = DateTime.Now.ToUnix(), PayChannel = dto.pay_channel,
                PayType = dto.pay_type, PayCode = dto.pay_code, PayName = pay_name, SettleType = dto.settle_type, TransactionNo = dto.transaction_no
            };

            context.Transfers.Add(model);
            return model;
        }

        private static readonly object dbwaitLock = new object();
        /// <summary>
        /// T0提现成功或不确定
        /// </summary>
        /// <returns>The success.</returns>
        /// <param name="dto">Dto.</param>
        public int TransferSuccess(platform_transfer dto) {
            lock (dbwaitLock) {
                var model = context.Transfers.FirstOrDefault(c => dto.order_no == c.OrderNo && c.Merchant.MerchantNo == dto.merchant_no);
                if (model.Status == TransferStatus.Finish)
                    throw new ServerErrorException("提现单" + dto.order_no + "已完成", 1002);
                model.Status = dto.status;
                model.PayInfo.TransactionNo = dto.transaction_no;
                model.PayInfo.PayTime = dto.pay_time.ToUnix();
                //代付成功账单状态修改
                if (dto.status == TransferStatus.Finish)
                    TransferAccounted(model.PayId);
                int result = _iUnitOfWork.Commit();
                if (result > 0) {
                    //创建通知消息对象
                    _iPayNotifyService.Save(new pay_notify_dto {
                        order_no = dto.order_no, pay_channel = model.PayInfo.PayChannel, type = 2, merchant_no = dto.merchant_no,
                        status = (int)model.Status, create_time = DateTime.Now
                    });
                }
                return result;
            }
        }

        /// <summary>
        /// T0提现失败
        /// </summary>
        /// <returns>The fail or un confirm.</returns>
        /// <param name="dto">Dto.</param>
        public int TransferFail(platform_transfer dto) {
            var model = context.Transfers.FirstOrDefault(c => dto.order_no == c.OrderNo && c.Merchant.MerchantNo == dto.merchant_no);
            if (model.Status == TransferStatus.Finish)
                throw new ServerErrorException(dto.order_no + "提现非申请状态", 1002);

            model.Status = dto.status;
            model.PayInfo.TransactionNo = dto.transaction_no;
            model.Fail = dto.fail;
            int result = _iUnitOfWork.Commit();
            if (result > 0) {
                //创建通知消息对象
                _iPayNotifyService.Save(new pay_notify_dto {
                    order_no = dto.order_no, pay_channel = model.PayInfo.PayChannel, type = 2, merchant_no = dto.merchant_no,
                    status = (int)model.Status, create_time = DateTime.Now
                });
            }
            return result;
        }

        /// <summary>
        /// 重新生成代付和收款信息
        /// </summary>
        /// <returns>The suceess.</returns>
        /// <param name="dto">Dto.</param>
        public int ReTransfer(re_transfer dto) {
            var model = context.Transfers.FirstOrDefault(c => dto.order_no == c.OrderNo && c.Merchant.MerchantNo == dto.merchant_no);
            if (model.Status == TransferStatus.Finish)
                throw new ServerErrorException("提现单" + dto.order_no + "非提现失败或不确定状态时", 1002);

            model.AccountNo = dto.auth_info.account_no;
            model.AccountName = dto.auth_info.account_name;
            model.IdCard = dto.auth_info.id_card;
            model.BankName = dto.auth_info.bank_name;
            model.BusiType = dto.auth_info.busi_type;
            model.Status = dto.status;
            //var oldPayInfo = model.PayInfo;
            var pay_no = model.OrderNo;
            if (!string.IsNullOrEmpty(dto.pay_no)) pay_no = dto.pay_no;
            model.PayInfo.PayNo = pay_no;
            model.PayInfo.TransactionNo = dto.transaction_no;
            if (dto.status == TransferStatus.Finish) {
                model.PayInfo.PayTime = (dto.pay_time ?? DateTime.Now).ToUnix();
                //账单是否存在，存在修改账单状态，不存在创建
                //if (context.BillDetails.Any(c => c.PayId == oldPayInfo.Id))
                //	TransferAccounted(oldPayInfo.Id, model.PayInfo);
                //else
                //	TransferAccount(model);
            } else {
                model.Fail = dto.fail;
                model.PayInfo.PayTime = (dto.pay_time ?? DateTime.Now).ToUnix();
            }

            int result = _iUnitOfWork.Commit();
            if (result > 0) {
                //创新或更新通知消息对象
                _iPayNotifyService.Save(new pay_notify_dto { merchant_no = dto.merchant_no, order_no = model.OrderNo, pay_channel = model.PayInfo.PayChannel, type = 2, status = (int)model.Status, create_time = DateTime.Now });
            }
            return result;
        }

        public transfer_batch_dto GetBatch(long batch_id) {
            var model = context.TransferBatchs.Find(batch_id);
            if (model != null)
                return model.ToDtoObject<transfer_batch_dto>();
            return null;
        }

        public transfer_batch_dto GetBatch(string batch_no) {
            var model = context.TransferBatchs.FirstOrDefault(c => c.BatchNo == batch_no);
            if (model != null)
                return model.ToDtoObject<transfer_batch_dto>();
            return null;
        }

        /// <summary>
        /// 批处理申请提现
        /// </summary>
        /// <returns>The transfer.</returns>
        /// <param name="batch_model">Batch model.</param>
        public long ApplyTransfer(batch_transfer batch_model) {
            if (batch_model.merchant_id == 0)
                throw new ServerErrorException("必须选择O商", 1001);
            if (batch_model.orders.Select(c => c.order_no).Distinct().Count() != batch_model.orders.Count)
                throw new ServerErrorException("存在相同的提现单号", 1001);
            var batch = new TransferBatch { PayChannel = batch_model.pay_channel, MerchantId = batch_model.merchant_id, BatchNo = batch_model.batch_no, Status = TransferBatchStatus.File, BatchFile = batch_model.batch_file };
            context.TransferBatchs.Add(batch);
            foreach (var dto in batch_model.orders) {
                Transfer model = context.Transfers.FirstOrDefault(c => c.OrderNo == dto.order_no && c.MerchantId == batch_model.merchant_id);
                if (model == null) throw new ServerErrorException("提现单不存在", 1001);
                model.PayInfo.TransactionNo = dto.transaction_no;
                if (!string.IsNullOrEmpty(dto.pay_no))
                    model.PayInfo.PayNo = dto.pay_no;
                model.Status = TransferStatus.Proceed;
                model.Batch = batch;
            }
            int result = _iUnitOfWork.Commit();
            if (result > 0) return batch.Id;
            return result;
        }

        /// <summary>
        /// 更新批处理状态
        /// </summary>
        /// <param name="batch_id">Batch identifier.</param>
        /// <param name="noticeTranId">报盘流水号</param>
        /// <param name="status">Status.</param>
        public int UpdateBatch(long batch_id, string noticeTranId, TransferBatchStatus status) {
            var model = context.TransferBatchs.Find(batch_id);
            model.Status = status;
            model.NoticeTranId = noticeTranId;

            int result = _iUnitOfWork.Commit();
            return result;
        }

        /// <summary>
        /// 批处理回盘、修改状态
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public int Transfer(List<platform_transfer> dto, long batch_id, TransferBatchStatus status, string backFile) {
            var batch = context.TransferBatchs.Find(batch_id);
            List<Transfer> list = new List<Transfer>();
            List<long> pay_ids = new List<long>();
            foreach (var item in dto) {
                Transfer model = null;
                if (backFile == "CHPAY")
                    model = context.Transfers.FirstOrDefault(c => c.PayInfo.PayNo == item.order_no && c.MerchantId == batch.MerchantId);
                else
                    model = context.Transfers.FirstOrDefault(c => c.PayInfo.TransactionNo == item.transaction_no && c.MerchantId == batch.MerchantId);
                item.order_no = model.OrderNo;
                model.Status = item.status;
                if (item.status == TransferStatus.Fail || item.status == TransferStatus.UnConfirm)
                    model.Fail = item.fail;
                if (!model.PayInfo.PayTime.HasValue)
                    model.PayInfo.PayTime = item.pay_time.ToUnix();
                model.PayInfo.TransactionNo = item.transaction_no;
                if (item.status == TransferStatus.Finish)
                    pay_ids.Add(model.PayId);
                list.Add(model);
            }
            //账单状态修改
            TransferAccounted(pay_ids);

            batch.BackFile = backFile;
            batch.Status = status;
            int result = _iUnitOfWork.Commit();
            if (result > 0) {
                //创新或更新通知消息对象
                list.DataForEach(transfer => {
                    if (!string.IsNullOrEmpty(transfer.Notify)) {
                        _iPayNotifyService.Save(new pay_notify_dto {
                            merchant_no = transfer.Merchant.MerchantNo, type = 2, status = (int)transfer.Status,
                            order_no = transfer.OrderNo, pay_channel = transfer.PayInfo.PayChannel, create_time = DateTime.Now
                        });
                    }
                });
            }
            return result;
        }

        /// <summary>
        /// 取消
        /// </summary>
        /// <returns>The cancel.</returns>
        /// <param name="id">Identifier.</param>
        public int Cancel(long id) {
            var model = context.Transfers.Find(id);
            model.Status = TransferStatus.Cancel;

            int result = _iUnitOfWork.Commit();
            return result;
        }
    }
}